#include <stdio.h>
#include <windows.h>
#include <tchar.h>

#include <GLES/egl.h>
#include <GLES/gl.h>

#include <FreeImage.h>

// Windows class name to register
#define	WINDOW_CLASS _T("PVRShellClass")

// Defines to abstract float/fixed data for Common/CommonLite profiles
#ifdef PVRT_FIXED_POINT_ENABLE
#define VERTTYPE		GLfixed
#define VERTTYPEENUM	GL_FIXED
#define f2vt(x)			((int)((x)*65536))
#define myglLoadMatrix	glLoadMatrixx
#define myglClearColor	glClearColorx
#else
#define VERTTYPE		GLfloat
#define VERTTYPEENUM	GL_FLOAT
#define f2vt(x)			(x)
#define myglLoadMatrix	glLoadMatrixf
#define myglClearColor	glClearColor
#endif

EGLDisplay			eglDisplay	= 0;
EGLSurface			eglSurface	= 0;
EGLConfig			  eglConfig	= 0;
EGLContext			eglContext	= 0;
NativeWindowType	eglWindow	= 0;

// Variable set in the message handler to finish the demo
bool	g_bDemoDone = false;


static FIBITMAP *src=0;
static GLubyte* fImage=0;

GLfloat texture_coords[] =
{
  0.0, 0.0,
  0.0, 1.0,
  1.0, 1.0,
};


static const GLbyte vertices[3*3]=
{
  -1, 1, 0,
  1,-1, 0,
  1, 1, 0
};

static const GLubyte colors[3*4]=
{
  255,  0,  0,  255,
  0,255,  0,  255,
  0,  0,255,  255
};

static GLfloat xRotate = 0.0;

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch (message)
  {
    // Handles the close message when a user clicks the quit icon of the window
  case WM_CLOSE:
    g_bDemoDone = true;
    PostQuitMessage(0);
    return 1;

  default:
    break;
  }

  // Calls the default window procedure for messages we did not handle
  return DefWindowProc(hWnd, message, wParam, lParam);
}

/*!****************************************************************************
@Function		TestEGLError
@Input			pszLocation		location in the program where the error took
place. ie: function name
@Return		bool			true if no EGL error was detected
@Description	Tests for an EGL error and prints it
******************************************************************************/
bool TestEGLError(HWND hWnd, char* pszLocation)
{
  /*
  eglGetError returns the last error that has happened using egl,
  not the status of the last called function. The user has to
  check after every single egl call or at least once every frame.
  */
  EGLint iErr = eglGetError();
  if (iErr != EGL_SUCCESS)
  {
    TCHAR pszStr[256];
    _stprintf(pszStr, _T("%s failed (%d).\n"), pszLocation, iErr);
    MessageBox(hWnd, pszStr, _T("Error"), MB_OK|MB_ICONEXCLAMATION);
    return false;
  }

  return true;
}

void SetupGLSystem()
{
  glShadeModel(GL_SMOOTH);
  glDisable(GL_CULL_FACE);
  glClearColor(0.f, 0.f, 0.1f, 1.f);
  glClearDepthf(1.0f);
  glEnable(GL_DEPTH_TEST);
  glDisable(GL_DEPTH_TEST);
  glDepthMask(1);
  glDepthFunc(GL_LESS);

  //glViewport(0, 0, width, height);
  glMatrixMode(GL_PROJECTION);
  glFrustumf(-1.0f, 1.0f, -1.0f, 1.0f, 3.0f, 1000.0f);
  glMatrixMode(GL_MODELVIEW);

  glEnable(GL_TEXTURE_2D);
}

void SetupSceneData()
{
  FreeImage_Initialise();

  const char* src_filename = "../../media/irrlichtlogo2.tga";
	FREE_IMAGE_FORMAT src_fif = FreeImage_GetFileType(src_filename);
	// load the file
	src = FreeImage_Load(src_fif, src_filename, 0); //24bit image 
  fImage = (GLubyte*)src->data;

  //make_texture_image();

  glVertexPointer(3, GL_BYTE, 0, vertices);
  //glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
  glTexCoordPointer(2, GL_FLOAT, 0, texture_coords);

  glEnableClientState(GL_VERTEX_ARRAY);
  //glEnableClientState(GL_COLOR_ARRAY);
  glEnableClientState(GL_TEXTURE_COORD_ARRAY);

  //int imgWidth = FreeImage_GetWidth(src_filename);
  //int imgHeight = FreeImage_GetHeight(src_filename);

  glTexImage2D(GL_TEXTURE_2D,
    0,
    GL_RGBA,
    128,
    128,
    0,
    GL_RGBA,
    GL_UNSIGNED_BYTE,
    fImage);

  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}

void SweepSceneData()
{
		FreeImage_Unload(src); 
    FreeImage_DeInitialise();
}

void RenderFrame()
{
  glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
  glLoadIdentity();

  glTranslatef(0,0,-5.0f);
  xRotate += 2.0;
  glRotatef(xRotate,0.0,1.0,0.0);

  //glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
  glDrawArrays(GL_TRIANGLES, 0, 3);
  glFinish();
  eglSwapBuffers(eglDisplay, eglSurface);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, TCHAR *lpCmdLine, int nCmdShow)
{
  int				i;
  unsigned int	nWidth=640, nHeight=480; // client size

  // Windows variables
  HWND				hWnd	= 0;
  HDC					hDC		= 0;

  // EGL variables
  EGLint				  pi32ConfigAttribs[128];

  /*
  Step 0 - Create a NativeWindowType that we can use for OpenGL ES output
  */
  // Register the windows class
  WNDCLASS sWC;
  sWC.style = CS_HREDRAW | CS_VREDRAW;
  sWC.lpfnWndProc = WndProc;
  sWC.cbClsExtra = 0;
  sWC.cbWndExtra = 0;
  sWC.hInstance = hInstance;
  sWC.hIcon = 0;
  sWC.hCursor = 0;
  sWC.lpszMenuName = 0;
  sWC.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
  sWC.lpszClassName = WINDOW_CLASS;
  ATOM registerClass = RegisterClass(&sWC);
  if (!registerClass)
  {
    MessageBox(0, _T("Failed to register the window class"), _T("Error"), MB_OK | MB_ICONEXCLAMATION);
  }

  // Create the eglWindow
  RECT	sRect;
  SetRect(&sRect, 0, 0, nWidth, nHeight);
  AdjustWindowRectEx(&sRect, WS_CAPTION | WS_SYSMENU, false, 0);
  hWnd = CreateWindow( WINDOW_CLASS, _T("HelloTriangle"), WS_VISIBLE | WS_SYSMENU,
    CW_USEDEFAULT, CW_USEDEFAULT, nWidth, nHeight, NULL, NULL, hInstance, NULL);
  eglWindow = hWnd;

  // Get the associated device context
  hDC = GetDC(hWnd);
  if (!hDC)
  {
    MessageBox(0, _T("Failed to create the device context"), _T("Error"), MB_OK|MB_ICONEXCLAMATION);
    goto cleanup;
  }

  /*
  Step 1 - Get the default display.
  EGL uses the concept of a "display" which in most environments
  corresponds to a single physical screen. Since we usually want
  to draw to the main screen or only have a single screen to begin
  with, we let EGL pick the default display.
  Querying other displays is platform specific.
  */
  eglDisplay = eglGetDisplay((NativeDisplayType) hDC);
  if(eglDisplay == EGL_NO_DISPLAY)
    eglDisplay = eglGetDisplay((NativeDisplayType) EGL_DEFAULT_DISPLAY);

  /*
  Step 2 - Initialize EGL.
  EGL has to be initialized with the display obtained in the
  previous step. We cannot use other EGL functions except
  eglGetDisplay and eglGetError before eglInitialize has been
  called.
  If we're not interested in the EGL version number we can just
  pass NULL for the second and third parameters.
  */
  EGLint iMajorVersion, iMinorVersion;
  if (!eglInitialize(eglDisplay, &iMajorVersion, &iMinorVersion))
  {
    MessageBox(0, _T("eglInitialize() failed."), _T("Error"), MB_OK|MB_ICONEXCLAMATION);
    goto cleanup;
  }

  /*
  Step 3 - Specify the required configuration attributes.
  An EGL "configuration" describes the pixel format and type of
  surfaces that can be used for drawing.
  For now we just want to use a 16 bit RGB surface that is a
  Window surface, i.e. it will be visible on screen. The list
  has to contain key/value pairs, terminated with EGL_NONE.
  */
  i = 0;
  pi32ConfigAttribs[i++] = EGL_RED_SIZE;
  pi32ConfigAttribs[i++] = 5;
  pi32ConfigAttribs[i++] = EGL_GREEN_SIZE;
  pi32ConfigAttribs[i++] = 6;
  pi32ConfigAttribs[i++] = EGL_BLUE_SIZE;
  pi32ConfigAttribs[i++] = 5;
  pi32ConfigAttribs[i++] = EGL_ALPHA_SIZE;
  pi32ConfigAttribs[i++] = 0;
  pi32ConfigAttribs[i++] = EGL_SURFACE_TYPE;
  pi32ConfigAttribs[i++] = EGL_WINDOW_BIT;
  pi32ConfigAttribs[i++] = EGL_NONE;

  /*
  Step 4 - Find a config that matches all requirements.
  eglChooseConfig provides a list of all available configurations
  that meet or exceed the requirements given as the second
  argument. In most cases we just want the first config that meets
  all criteria, so we can limit the number of configs returned to 1.
  */
  int iConfigs;
  if (!eglChooseConfig(eglDisplay, pi32ConfigAttribs, &eglConfig, 1, &iConfigs) || (iConfigs != 1))
  {
    MessageBox(0, _T("eglChooseConfig() failed."), _T("Error"), MB_OK|MB_ICONEXCLAMATION);
    goto cleanup;
  }

  /*
  Step 5 - Create a surface to draw to.
  Use the config picked in the previous step and the native window
  handle when available to create a window surface. A window surface
  is one that will be visible on screen inside the native display (or
  fullscreen if there is no windowing system).
  Pixmaps and pbuffers are surfaces which only exist in off-screen
  memory.
  */
  eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, eglWindow, NULL);

  if(eglSurface == EGL_NO_SURFACE)
  {
    eglGetError(); // Clear error
    eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, NULL, NULL);
  }

  if (!TestEGLError(hWnd, "eglCreateWindowSurface"))
  {
    goto cleanup;
  }

  /*
  Step 6 - Create a context.
  EGL has to create a context for OpenGL ES. Our OpenGL ES resources
  like textures will only be valid inside this context
  (or shared contexts)
  */
  eglContext = eglCreateContext(eglDisplay, eglConfig, NULL, NULL);
  if (!TestEGLError(hWnd, "eglCreateContext"))
  {
    goto cleanup;
  }

  /*
  Step 7 - Bind the context to the current thread and use our
  window surface for drawing and reading.
  Contexts are bound to a thread. This means you don't have to
  worry about other threads and processes interfering with your
  OpenGL ES application.
  We need to specify a surface that will be the target of all
  subsequent drawing operations, and one that will be the source
  of read operations. They can be the same surface.
  */
  eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
  if (!TestEGLError(hWnd, "eglMakeCurrent"))
  {
    goto cleanup;
  }

  /*
  Step 8 - Draw something with OpenGL ES.
  At this point everything is initialized and we're ready to use
  OpenGL ES to draw something on the screen.
  */
  SetupGLSystem();

  SetupSceneData();

  // Sets the clear color.
  // The colours are passed per channel (red,green,blue,alpha) as float values from 0.0 to 1.0
  //myglClearColor(f2vt(0.6f), f2vt(0.8f), f2vt(1.0f), f2vt(1.0f)); // clear blue

  // Draws a triangle for 800 frames
  for(i = 0; i < 800; ++i)
  {
    if(g_bDemoDone)
      break;

    RenderFrame();

    // Managing the window messages
    MSG msg;
    PeekMessage(&msg, hWnd, NULL, NULL, PM_REMOVE);
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  /*
  Step 9 - Terminate OpenGL ES and destroy the window (if present).
  eglTerminate takes care of destroying any context or surface created
  with this display, so we don't need to call eglDestroySurface or
  eglDestroyContext here.
  */
cleanup:
  eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
  eglTerminate(eglDisplay);

  /*
  Step 10 - Destroy the eglWindow.
  Again, this is platform specific and delegated to a separate function.
  */
  // Release the device context
  if (hDC) ReleaseDC(hWnd, hDC);
  // Destroy the eglWindow
  if (hWnd) DestroyWindow(hWnd);

  return 0;
}
